#!/usr/bin/python3


import requests
from pyasn1.codec.der import decoder, encoder
import pyasn1
import urllib
import base64
import hashlib
import binascii


TARGET = "http://spkac.hackable.software:8080/"
secret = "readme"
# use openssl pkcs12 -in some.cert -nodes -out private_key_file to convert a certificate file to PEM
private_key_file = "private-key.pem"
proxies = {'http': "http://127.0.0.1:8080"}
#put your captured SignedPublicKeyAndChallenge structure here:
security_string = "MIICRjCCAS4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCm8I%2Bw%2BzQf%0D%0AljxufmLlH%2FOA0SNo9%2Bc5Aa%2FZwRaJOB5zCJiviIC4FupF7zdP6ExjH5drjtgwi1TT%0D%0AvHqMgLzqyX%2FxCJV76t%2B6%2FKGnnvZk7%2BYhJ%2BdCM1U%2BAbpHcbzFeDOWzlwNF6Kzpgq5%0D%0A8MTCFRRqU5xo4RM574Mggnq1%2Fw95obOdfimpR3mUWm%2BP1%2Bavn4RmL%2FGdppRKFXum%0D%0AaKswzfwzIyc4ZVMPFZIbtPpjpwOKU9qNlpyh1aBR8umiNIcpo1%2BBmLWUNBl0ZG5U%0D%0ARPnJKkyXmwrub5yPVRL5jcD5s9CIlqTf%2B8IykBxxl%2Bz676FU44VDjShJ%2FAVjD7oZ%0D%0AgTSn%2FGQ2ixFPAgMBAAEWBnJlYWRtZTANBgkqhkiG9w0BAQQFAAOCAQEAjPZaDF8g%0D%0An1GUJlRN7Zvkf%2FCjyH50d4PG2QE9O3o2p%2FaDh504KlO96ASjcxpmTgrQaD8KZ36v%0D%0AgZbDf3E%2F9CNCOErDljPJlzluRrdcgRxtZKYZs7278kpjPhf0jUT1cJyEQMXfZcSx%0D%0AiJpqd7jcNfMZHZw4H2juH6oZtW10Re1pSU4bN7eo%2BbiPyIuLfXQSK5q1d2RUoFHr%0D%0Auert1uBX5zCX07SjfwVlDo2ZI8v9tZ%2BVgzN0xdLDVoebyfzvOBI1KdcuOOeY9GX0%0D%0ADckd3do1gEMgz4ze1NqNA%2FS%2FLZLZdrKXxEH3qb7sJCLzXppYslq8Au0VIPcpvzma%0D%0ANYllZBJErzbIbw%3D%3D"


#see https://www.w3.org/TR/html5/forms.html#the-keygen-element for a description of what's going on


#implement some functions for the code copied from the challenge. Used for toying around.
def getSignature(der):
	return bitstring2Int(der[2])

def getN(der):
	return bitstring2Int(der[0][0][1])
	
def bitstring2Int(bitstring):
	return int(''.join(map(str, bitstring)), 2)



#copied from the challenge:
def dataToVerify(der, secret):
  # We can't trust secret sent by browser, user can modify it !
  der[0][1] = pyasn1.type.char.IA5String(secret)
  return encoder.encode(der[0])

def cmpMin(a,b):
  minLen = min(len(a),len(b))
  if minLen < 1:
    # empty string ? !
    return False

  for i in range(minLen):
    if a[::-1][i] != b[::-1][i]:
       return False
  
  return True

def verify(der, secret):
  md5rsa = pow(getSignature(der), 65537, getN(der))
  md5cmp = ''
  while md5rsa:
    md5cmp = chr(md5rsa % 256) + md5cmp
    md5rsa //= 256
  # md5cmp is with padding, so compare only up to hash length
  return cmpMin(md5cmp, hashlib.md5(dataToVerify(der, secret)).digest())


def extractSecretKeyFromPem(filename):
	
	#see: http://www.cryptopp.com/wiki/Keys_and_Formats#RSA_PrivateKey
	
	content = open(filename, 'r').read()
	prefix = "\n-----BEGIN PRIVATE KEY-----\n"
	suffix = "\n-----END PRIVATE KEY-----\n"
	
	start = content.index(prefix) + len(prefix)
	end = content.index(suffix)
	
	key = content[start:end]
	key = base64.b64decode(key)
	
	key = decoder.decode(key)
	
	key = key[0]
	version = key[0]
	if not version == 0:
		raise ValueError("unknown key format version: " + version + \
		", try openssl pkey -in " + filename + " -text or something")
	
	oid = str(key[1][0])
	if not oid in ["1.2.840.113549.1.1.1", "1.2.840.113549.1.1.4"]:
		raise ValueError("unknown key type. Object Identifier: " + oid)
		
	key = key[2]
	key = decoder.decode(key)
	key = key[0]
	
	old_version = version
	version = key[0]
	if not version == old_version:
		raise ValueError("different versions detected: " + \
		str(old_version) + " and " + str(version) + ". This is strange.")
	
	modulus = int(key[1])
	public_exponent = int(key[2])
	private_exponent = int(key[3])
	prime1 = int(key[4])
	prime2 = int(key[5])
	exponent1 = int(key[6])
	exponent2 = int(key[7])
	coefficient = int(key[8])
	
	key = {
		'modulus': modulus,
		'public_exponent': public_exponent,
		'private_exponent': private_exponent,
		'prime1': prime1,
		'prime2': prime2,
		'exponent1': exponent1,
		'exponent2': exponent2,
		'coefficient': coefficient
	}
	return key

def checkPrivateKey(private_key):
	"""does a few sanity checks on an RSA private key."""
	p = private_key['prime1']
	q = private_key['prime2']
	N = private_key['modulus']
	e = private_key['public_exponent']
	d = private_key['private_exponent']
	# the exponents and coefficients for CRT should be checked here...
	return \
		p * q == N and \
		(e * d) % ((p-1)*(q-1)) == 1
	
	
	
if __name__ == "__main__":
	
	#extract the SignedPublicKeyAndChallenge
	der_string = urllib.parse.unquote(security_string) # strip urlencoding
	der_string = base64.b64decode(der_string) # strip base64 encoding
	original_der = decoder.decode(der_string) # strip ASN.1 encoding
	der = original_der[0]
	signedPublicKeyAndChallenge = der
	
	# look more into the SignedPublicKeyAndChallenge.
	# This is probably not needed.
	publicKeyAndChallenge = signedPublicKeyAndChallenge[0]
	signatureAlgorithm = signedPublicKeyAndChallenge[1]
	signature = signedPublicKeyAndChallenge[2]
	print(str(signature))
	signatureTag = signature.getTagSet()[0]
	print(type(signatureTag), signatureTag)
	signature = bitstring2Int(signature)
	
	subjectPublicKeyInfo = publicKeyAndChallenge[0]
	challenge = publicKeyAndChallenge[1]
	
	algorithm = subjectPublicKeyInfo[0]
	publicKey = subjectPublicKeyInfo[1]
	N = bitstring2Int(publicKey)
	
	# get the private key from the .pem file
	key = extractSecretKeyFromPem(private_key_file)
	if not checkPrivateKey(key):
		raise ValueError("strange key")
	#assert(N == key['modulus'])
	
	#used to automatically apply the required ASN.1 tags to the signature.
	class Signature(pyasn1.type.univ.BitString):
		tagSet = pyasn1.type.univ.BitString.tagSet.tagImplicitly(signatureTag)
	
	# try to find the last byte of the unknown md5 hash:
	for i in range(0, 256):
		signature = pow(i, key['private_exponent'], key['modulus']) #compute the signature
		signature = "'{0:b}'B".format(signature) # convert to binary
		signature = Signature(signature) # make it an ASN.1 object
		original_der[0][2] = signature # replace the signature from the capture
		
		der = encoder.encode(original_der[0]) # ASN.1 encode
		der = base64.b64encode(der) # base64 encode
		r = requests.post(TARGET, data = {'mid':'flag', 'security': der}, proxies=proxies)
		print(r.text)
		
		
		
# DrgnS{7H1s_FL49_91V3s_PO1N72}
